package es.uji.viselab.experiment;

import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.swing.JPanel;

import es.uji.viselab.ViSeLabComponent;
import es.uji.viselab.benchmark.BenchMarkingRecorder;
import es.uji.viselab.benchmark.database.DBConnection;
import es.uji.viselab.benchmark.database.DBNonPersistent;
import es.uji.viselab.benchmark.database.DBNull;
import es.uji.viselab.benchmark.database.DatabaseCatalog;
import es.uji.viselab.benchmark.process.BenchMarkingProcessor;
import es.uji.viselab.benchmark.process.BenchmarkProcessorCatalog;
import es.uji.viselab.benchmark.process.StatsProcessor;
import es.uji.viselab.experiment.control.ExperimentCatalog;
import es.uji.viselab.experiment.control.ExperimentControl;
import es.uji.viselab.experiment.control.IBVS_Control_loop;
import es.uji.viselab.experiment.control.PBVS_Control;
import es.uji.viselab.image.process.ImageProcessCatalog;
import es.uji.viselab.image.process.ImageProcessing;
import es.uji.viselab.image.source.ImageCameraSimulated;
import es.uji.viselab.image.source.ImageSourceCatalog;
import es.uji.viselab.image.source.ImageListener;
import es.uji.viselab.image.source.ImageSource;
import es.uji.viselab.math.Matrix4;
import es.uji.viselab.robot.Robot;
import es.uji.viselab.robot.RobotCatalog;
import es.uji.viselab.robot.SimulatedRobot;
import es.uji.viselab.sceneview.SceneView;
import es.uji.viselab.sceneview.SimpleRobotScene;
import es.uji.viselab.util.LogLevel;
import es.uji.viselab.util.LogRecorder;
import es.uji.viselab.visualservoing.SimpleIBVS;
import es.uji.viselab.visualservoing.SimplePBVS;
import es.uji.viselab.visualservoing.VSCatalog;
import es.uji.viselab.visualservoing.VisualServoingTask;
import es.uji.viselab.world.TargetSimulatedSelection;
import es.uji.viselab.world.World;

public abstract class ExperimentInterface implements ImageListener, DataArrivalListener,
		ViSeLabComponent, ActionListener {

	protected ExperimentParameters ep;
	protected VisualServoingTask visualServoing;
	protected Robot robot;
	protected SceneView scene;
	protected World world;
	protected ImageSource imageSource;
	protected ImageProcessing imageProcessor;
	protected ExperimentControl experiment;
	protected BenchMarkingProcessor benchmarkingProcessor;
	protected BenchMarkingRecorder benchmarkingRecorder;
	protected DBConnection dbConnection;

	public ExperimentInterface(ExperimentParameters ep) {
		this.ep = ep;
	}
	
	
	protected Set<ExperimentListener> notifiers = new HashSet<ExperimentListener>();

	protected void notifyExperimentStart(ExperimentInterface ei) {
		Iterator<ExperimentListener> iel = notifiers.iterator();
		while (iel.hasNext()) {
			ExperimentListener el = iel.next();
			el.experimentStart(ei);
		}
	}

	public void addListener(ExperimentListener el) {
		notifiers.add(el);
	}

	public abstract JPanel getPanel();

	@Override
	public void newConnectionStablished() {
	}

	@Override
	public void cameraFeaturePointsChanged(ImageSource i) {
		if (experiment != null)
			experiment.cameraFeaturePointsChanged(i);
	}

	public void setRobot(Robot robot) {
		this.robot = robot;
	}

	public void setVSTask(VisualServoingTask vs) {
		this.visualServoing = vs;
	}

	public abstract void start(); 

	public void createSceneView() {

		if (scene == null) {
			if (robot == null)
				createRobot();
			switch (robot.getCurrentRobotModel()) {
			case FLYING_CAMERA:
				scene = new SimpleRobotScene(ep);
				break;
			case VABBIRB140:
				// scene = new VirtualRobotScene(robot.getCurrentRobotModel());
				break;
			case ABBIRB140:
				// scene = new VirtualRobotScene(robot.getCurrentRobotModel());
				break;
			case FANUCARC120:
				scene = new SimpleRobotScene(ep);
				break;
			case VFANUCARC120:
				scene = new SimpleRobotScene(ep);
				break;
			}
			if (world != null) {
				world.addListener(scene);
			}
			if (robot != null) {
				robot.addListener(scene);
			}
		}
	}

	public void createComponents() {

		createBenchmarkingRecorder();
		createImageSource();
		createImageProcessor();
		createRobot();
		createVSTask();
		createBenchmarkingProcessor();
		createDatabase();
		createExperiment();
		

	}

	public void initializeComponents() {
		createComponents();
		experiment.setRobot(robot);
		experiment.setVisualServoing(visualServoing);
	}

	private void createBenchmarkingRecorder() {

		benchmarkingRecorder = new BenchMarkingRecorder();
		if (dbConnection != null)
			benchmarkingRecorder.setDatabase(dbConnection);
		if (visualServoing!=null)
			visualServoing.addListener(benchmarkingRecorder);
		this.addListener(benchmarkingRecorder);
	}

	private void createExperiment() {
		if (experiment == null) {
			ExperimentCatalog ec = ep.getExperiment();

			switch (ec) {
			case CONTROLLOOP_POS200:
				experiment = new IBVS_Control_loop(ep);
				break;
			case CONTROLLOOP_PBVS_POS200:
				experiment = new PBVS_Control(ep);
				break;
			}
			if (robot != null)
				experiment.setRobot(robot);
			if (imageSource!=null)
				experiment.setImageSource(imageSource);
			
		}

	}

	public void createImageSource() {

		if (imageSource == null) {

			ImageSourceCatalog imageCatalog = ep.getImageSource();

			switch (imageCatalog) {
			case KINECT:
				// imageSource = new Kinect(ep);
				break;
			case VIRTUALCAMERA:
				imageSource = new ImageCameraSimulated(ep);
				break;
			case CAMERAURL:
				// imageSource = new URLCamera(ep);
				break;
			default:
				LogRecorder.log(LogLevel.ERROR, "Select an available camera");
			}
			imageSource.addListener(this);
			if (visualServoing != null)
				imageSource.addListener(visualServoing);
			if (robot != null)
				robot.addListener(imageSource);
			if (world != null)
				world.addListener(imageSource);
			if (imageProcessor != null)
				imageSource.setProcessing(imageProcessor);
			if (experiment!=null)
				experiment.setImageSource(imageSource);
		}

	}

	public void createImageProcessor() {
		if (imageProcessor == null) {
			ImageProcessCatalog processCatalog = ep.getImageProcessor();

			switch (processCatalog) {
			case OPENCVCHESSBOARD:
				// imageProcessor = new ProcessChessBoard();
				break;
			case VIRTUAL:
				imageProcessor = null;
			}
			if (imageSource != null)
				imageSource.setProcessing(imageProcessor);
		}
	}

	public void createVSTask() {

		if (visualServoing == null) {
			VSCatalog selectedVS = ep.getVSMethod();
			switch (selectedVS) {
			case IBVS:
				// visualServoingTask = new IBVS(ep);
				break;
			case IBVSSIMPLIFIED:
				visualServoing = new SimpleIBVS(ep);
				break;
			case PBVSSIMPLIFIED:
				visualServoing = new SimplePBVS(ep);
				break;
			case VISP:
				// visualServoingTask = new VsVispControlInterface(ep);
				break;
			case RELATIVE:
				// visualServoingTask = new PBVSEyeInHandRelative(ep);
				break;
			}
			if (visualServoing != null) {
				if (imageSource != null)
					imageSource.addListener(visualServoing);
				if (benchmarkingRecorder!=null)
					visualServoing.addListener(benchmarkingRecorder);
			} else
				LogRecorder.log(LogLevel.ERROR, "It has not been possible to create the VS task");
		}
	}

	public void createRobot() {
		RobotCatalog currentRobotModel = ep.getRobotModel();
		switch (currentRobotModel) {
		case ABBIRB140:
			// robot = new RealRobot(currentRobotModel);
			break;
		case VABBIRB140:
			robot = new SimulatedRobot(currentRobotModel);
			break;
		case FLYING_CAMERA:
			robot = new SimulatedRobot(currentRobotModel);
			break;
		case FANUCARC120:
			// robot = new RobotLinkFanucARCMate120(currentRobotModel);
			break;
		case VFANUCARC120:
			// robot = new RobotFanucARCMate120(currentRobotModel);
			break;
		}
		if (robot != null) {
			if (scene != null)
				robot.addListener(scene);
			if (imageSource != null)
				robot.addListener(imageSource);
			if (benchmarkingRecorder != null)
				robot.addListener(benchmarkingRecorder);
			LogRecorder.log(LogLevel.INFO, "Robot " + currentRobotModel.toString()
					+ " created correctly");
		} else
			LogRecorder.log(LogLevel.ERROR, "Robot " + currentRobotModel.toString()
					+ " not created", "ViSeLabMainMenu.createRobot();");
	}

	public void createDatabase() {

		if (dbConnection == null) {
			DatabaseCatalog databaseCatalog = ep.getDatabase();
			switch (databaseCatalog) {
			case MEMORY:
				dbConnection = new DBNonPersistent();
				break;
			case MYSQL:
				// dbConnection = new DBConnectionMySQL();
				break;
			case NONE:
				dbConnection = new DBNull();
			}
			if (benchmarkingRecorder != null)
				benchmarkingRecorder.setDatabase(dbConnection);
		}
	}

	public void createBenchmarkingProcessor() {

		if (benchmarkingProcessor == null) {
			BenchmarkProcessorCatalog processor = ep.getBenchmarkingProcessor();

			switch (processor) {
			case GNUPlot:
				// benchmarkingProcessor = new GNUPlot();
				break;
			case JPLOT:
				// benchmarkingProcessor = new JPlot();
				break;
			case STATS:
				benchmarkingProcessor = new StatsProcessor();
				break;
			}

		}
	}

	public void createTargetSimulatedSelection() {
		Matrix4 initialValues = ep.getTarget();
		world = new TargetSimulatedSelection(initialValues);
		if (scene != null)
			world.addListener(scene);
		if (imageSource != null)
			world.addListener(imageSource);
	}

	public SceneView getScene() {
		return scene;
	}

	public World getWorld() {
		return world;
	}

	public DBConnection getDbConnection() {
		return dbConnection;
	}

	public Robot getRobot() {
		return robot;
	}

	public ImageSource getImageSource() {
		return imageSource;
	}

	public BenchMarkingProcessor getBenchmarkingProcessor() {
		return benchmarkingProcessor;
	}

	public VisualServoingTask getVisualServoingTask() {
		return visualServoing;
	}

	public void registerNewExperiment() {
		ep.setExperimentId(dbConnection.nextExperimentId());
		notifyExperimentStart(this);
	}

	public ExperimentParameters getExperimentParameters() {
		return ep;
	}

}
